﻿/*------------------------------------------------------------------
-- COPYRIGHT (C) 2010-2012  Atom
-- ALL RIGHTS RESERVED.
-- 兆尹科技
-- CREATE DATE: 2010/07/12
-- CREATE MAN:liujian	
-- 为外部创建DataAccess的实例，并提供常用的数据访问方法类
-- MODIFY HISTORY:
-- MODIFY DATE:
-- MODIFY MAN:	
-- MODIFY DESC:
-- MODIFY DATE:
-- MODIFY MAN:	
-- MODIFY DESC:
---------------------------------------------------------------------*/
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using Atom.Utility;
using Atom.Utility.Data;
using Atom.Common;
using Atom.Entity;

namespace Atom.DAL
{
    /// <summary>
    /// 为外部创建DataAccess的实例，并提供常用的数据访问方法。
    /// </summary>
    public static class DAOManager
    {
        #region Create
        /// <summary>
        /// 创建DataAccessObject对象
        /// </summary>
        /// <typeparam name="T">DataAccessObject对象类型</typeparam>
        /// <returns>返回DataAccessObject实例</returns>
        public static T Create<T>()
            where T : BaseDAL, new()
        {
            return ObjectCreator.Create<T>();
        } 
        #endregion

        #region RollbackTransaction
        /// <summary>
        /// 对事物执行Rollback回滚
        /// </summary>
        /// <param name="trans">事务对象</param>
        /// <remarks>多次对同一个事务对象，直接调用Rollback方法的时候，会产生异常。DbTransaction.Rollback回滚事务对象，可以避免多次调用而产生的异常。</remarks>
        public static void RollbackTransaction(DbTransaction trans)
        {
            if (trans != null && trans.Connection != null)
            {
                //trans.Commit();
                trans.Rollback();
            }
        } 
        #endregion

        #region ExecuteTransaction

        /// <summary>
        /// 对某一个动作，执行事务
        /// </summary>
        /// <param name="actionTransaction">执行事务的动作</param>
        /// <remarks>如果参数trans不为null的话：调用此方法的过程中，trans是不会做Commit，必须方法外部做Commit；但如果出现异常，trans会执行Rollback。</remarks>
        public static SqlExecuteResult ExecuteTransaction(Func<DbTransaction, SqlExecuteResult> actionTransaction)
        {
            return ExecuteTransaction(BaseDAL.CreateDataAccess(BaseDAL.DEFALUT_DATABASE_NAME)
                , actionTransaction);
        }

        /// <summary>
        /// 对某一个动作，执行事务
        /// </summary>
        /// <param name="db">数据库工具类</param>
        /// <param name="actionTransaction">执行事务的动作</param>
        /// <remarks>如果参数trans不为null的话：调用此方法的过程中，trans是不会做Commit，必须方法外部做Commit；但如果出现异常，trans会执行Rollback。</remarks>
        public static SqlExecuteResult ExecuteTransaction(Database db, Func<DbTransaction, SqlExecuteResult> actionTransaction)
        {
            SqlExecuteResult result = null;

            if (actionTransaction != null)
            {
                using (DbConnection conn = db.CreateConnection())
                {
                    conn.Open();
                    DbTransaction trans = conn.BeginTransaction();

                    try
                    {
                        result = actionTransaction(trans);
                        if (result == null || !result.Success)
                        {
                            DAOManager.RollbackTransaction(trans);
                        }
                        else
                        {
                            trans.Commit();
                        }
                    }
                    catch (Exception ex)
                    {
                        DAOManager.RollbackTransaction(trans);
                        //捕捉了异常，并把异常封装在SqlExecuteResult中
                        result = new SqlExecuteResult()
                        {
                            Success = false,
                            Exception = ex
                        };
                    }
                    finally
                    {
                        conn.Close();
                    }
                }
            }
            return result;
        } 
        #endregion

        #region LoadTableSchema
        private static object lockObjLoadTableSchema = new object();
        /// <summary>
        /// 缓存表结构
        /// </summary>
        /// <remarks>
        /// Key为字段名称（大写）；Value为字段信息
        /// </remarks>
        private static Dictionary<string, Dictionary<string, DatabaseColumnStruct>> tableSchemasCache =
            new Dictionary<string, Dictionary<string, DatabaseColumnStruct>>();

        /// <summary>
        /// 读取表结构
        /// </summary>
        /// <param name="tableName">表名称</param>
        /// <param name="db">数据库访问工具类</param>
        /// <returns>字段信息列表</returns>
        public static Dictionary<string, DatabaseColumnStruct> LoadTableSchema(string tableName, Database db)
        {
            tableName = tableName.ToUpper();

            if (!tableSchemasCache.ContainsKey(tableName))
            {
                lock (lockObjLoadTableSchema)
                {
                    Dictionary<string, DatabaseColumnStruct> cols = new Dictionary<string, DatabaseColumnStruct>();
                    //读取表结构，并加载到缓存中
                    DataTable schemaInfos = new DataTable();
                    string sql = string.Format("SELECT TOP 0 * FROM [{0}] ", tableName);
                    using (IDataReader dr = db.ExecuteReader(db.GetSqlStringCommand(sql)))
                    {
                        schemaInfos = dr.GetSchemaTable();
                    }
                    foreach (DataRow schemaInfo in schemaInfos.Rows)
                    {
                        cols.Add(schemaInfo["ColumnName"].ToString().ToUpper()      //字段名称为Key
                            , new DatabaseColumnStruct()
                            {
                                ColumnIndex = Convert.ToInt32(schemaInfo["ColumnOrdinal"]),
                                DbType = (Type)schemaInfo["DataType"],
                                DbTypeName = schemaInfo["DataTypeName"].ToString(),
                                AllowDBNull = (bool)schemaInfo["AllowDBNull"],
                                IsIdentity = (bool)schemaInfo["IsIdentity"],
                                Name = schemaInfo["ColumnName"].ToString(),
                                Size = (int)schemaInfo["ColumnSize"]
                            });
                    }

                    tableSchemasCache.Add(tableName, cols);
                }
            }

            return tableSchemasCache[tableName];
        } 
        #endregion
    }
}
